﻿/**
 * Copyright (C) 2011 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;
using NGuice.Inject.Binder;
using NGuice.Inject.Internal.Util;

namespace NGuice.Inject.Internal
{
    //complete!
    /// <summary>
    /// 引用一个scope，可以是直接引用（scope实例），或者间接引用（scope元注释）
    /// </summary>
    public abstract class Scoping
    {
        public static readonly Scoping UNSCOPED = new UnscopedScoping();

        public static readonly Scoping EAGER_SINGLETON = new EagerSingletonScoping();

        public static readonly Scoping SINGLETON_ANNOTATION = new SingletonAnnotationScoping();

        public static readonly Scoping SINGLETON_INSTANCE = new SingletonInstanceScoping();

        private Scoping() { }

        //private Scoping(Type scopingAttributeType) { }

        public abstract V AcceptVisitor<V>(IBindingScopingVisitor<V> visitor);

        public abstract void ApplyTo(IScopedBindingBuilder scopedBindingBuilder);

        public static Scoping ForInstance(IScope scope)
        {
            if (scope == Scopes.SINGLETON)
            {
                return SINGLETON_INSTANCE;
            }
            return new ScopedScoping(scope);
        }

        public static Scoping ForAnnotation<T>()
            where T : Attribute
        {
            return ForAnnotation(typeof(T));
        }

        public static Scoping ForAnnotation(Type scopeAttribute)
        {
            if (scopeAttribute == typeof(SingletonAttribute))
            {
                return SINGLETON_ANNOTATION;
            }
            return new AttributeScoping(scopeAttribute);
        }

        /// <summary>
        /// 获取scope实例，如果不知道则返回null
        /// </summary>
        public virtual IScope ScopeInstance
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// 如果此scope显式的被应用，则返回true。如果没有scope被显式应用，scoping元注释将被使用
        /// </summary>
        public virtual bool IsExplicitlyScoped
        {
            get
            {
                return this != UNSCOPED;
            }
        }

        /// <summary>
        /// 如果此scope为默认的scope则返回true，默认scope的情形，将为每一次注入提供一个新的实例
        /// </summary>
        public virtual bool IsNoScope
        {
            get
            {
                return ScopeInstance == Scopes.NO_SCOPE;
            }
        }

        /// <summary>
        /// 如果此scope为单例且在stage中可以预先加载则返回true
        /// </summary>
        /// <param name="stage"></param>
        /// <returns></returns>
        public virtual bool IsEagerSingleton(Stage stage)
        {
            if (this == EAGER_SINGLETON)
            {
                return true;
            }

            if (stage == Stage.PRODUCTION)
            {
                return this == SINGLETON_ANNOTATION || this == SINGLETON_INSTANCE;
            }
            return false;
        }

        /// <summary>
        /// 获取scope元注释类型，如果不知晓此实例则返回null
        /// </summary>
        public virtual Type ScopeAnnotation
        {
            get
            {
                return null;
            }
        }

        public override bool Equals(object obj)
        {
            if (obj is Scoping)
            {
                Scoping o = (Scoping)obj;
                return Objects.Equal(ScopeAnnotation, o.ScopeAnnotation) && Objects.Equal(ScopeInstance, o.ScopeInstance);
            }
            else
            {
                return false;
            }
        }

        public override int GetHashCode()
        {
            return Objects.GetHashCode(ScopeAnnotation, ScopeInstance);
        }

        /// <summary>
        /// 范围化内部工厂
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="injector"></param>
        /// <param name="creator"></param>
        /// <param name="source"></param>
        /// <param name="scoping"></param>
        /// <returns></returns>
        internal static InternalFactory<T> Scope<T>(Key<T> key, InjectorImpl injector, InternalFactory<T> creator, object source, Scoping scoping)
        {
            if (scoping.IsNoScope)
            {
                return creator;
            }
            IScope scope = scoping.ScopeInstance;
            IProvider<T> scoped = scope.Scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
            return new InternalFactoryToProviderAdapter<T>(Initializables.Of<IProvider<T>>(scoped), source);
        }


        internal static Scoping MakeInjectable(Scoping scoping, InjectorImpl injector, Errors errors)
        {
            Type scopeAnnotation = scoping.ScopeAnnotation;
            if (scopeAnnotation == null)
            {
                return scoping;
            }

            IScope scope = injector.state.GetScope(scopeAnnotation);
            if (scope != null)
            {
                return ForInstance(scope);
            }

            errors.ScopeNotFound(scopeAnnotation);
            return UNSCOPED;
        }

        #region 内部实现的Scoping类
        private class ScopedScoping : Scoping
        {
            private IScope scope;

            internal ScopedScoping(IScope scope)
            {
                this.scope = scope;
            }

            public override IScope ScopeInstance
            {
                get
                {
                    return this.scope;
                }
            }

            public override string ToString()
            {
                return this.scope.ToString();
            }

            public override V AcceptVisitor<V>(IBindingScopingVisitor<V> visitor)
            {
                return visitor.VisitScope(scope);
            }

            public override void ApplyTo(IScopedBindingBuilder scopedBindingBuilder)
            {
                scopedBindingBuilder.In(this.scope);
            }
        }

        private class AttributeScoping : Scoping
        {
            private Type scopingAttributeType;

            internal AttributeScoping(Type scopingAttributeType)
            {
                this.scopingAttributeType = scopingAttributeType;
            }

            public override Type ScopeAnnotation
            {
                get
                {
                    return scopingAttributeType;
                }
            }

            public override string ToString()
            {
                return scopingAttributeType.Name;
            }

            public override V AcceptVisitor<V>(IBindingScopingVisitor<V> visitor)
            {
                return visitor.VisitScopeAnnotation(scopingAttributeType);
            }

            public override void ApplyTo(IScopedBindingBuilder scopedBindingBuilder)
            {
                scopedBindingBuilder.In(scopingAttributeType);
            }
        }


        private class UnscopedScoping : Scoping
        {
            public override V AcceptVisitor<V>(IBindingScopingVisitor<V> visitor)
            {
                return visitor.VisitNoScoping();
            }

            public override void ApplyTo(IScopedBindingBuilder scopedBindingBuilder)
            {
                //do noting;
            }

            public override IScope ScopeInstance
            {
                get
                {
                    return Scopes.NO_SCOPE;
                }
            }

            public override string ToString()
            {
                return Scopes.NO_SCOPE.ToString();
            }
        }

        private class EagerSingletonScoping : Scoping
        {
            public override IScope ScopeInstance
            {
                get
                {
                    return Scopes.SINGLETON;
                }
            }

            public override string ToString()
            {
                return "eager singleton";
            }

            public override V AcceptVisitor<V>(IBindingScopingVisitor<V> visitor)
            {
                return visitor.VisitEagerSingleton();
            }

            public override void ApplyTo(IScopedBindingBuilder scopedBindingBuilder)
            {
                scopedBindingBuilder.AsEagerSingleton();
            }
        }

        private class SingletonAnnotationScoping : Scoping
        {
            public override V AcceptVisitor<V>(IBindingScopingVisitor<V> visitor)
            {
                return visitor.VisitScopeAnnotation(typeof(SingletonAttribute));
            }

            public override void ApplyTo(IScopedBindingBuilder scopedBindingBuilder)
            {
                scopedBindingBuilder.In(typeof(SingletonAttribute));
            }

            public override string ToString()
            {
                return typeof(SingletonAttribute).Name;
            }

            public override Type ScopeAnnotation
            {
                get
                {
                    return typeof(SingletonAttribute);
                }
            }
        }

        private class SingletonInstanceScoping : Scoping
        {
            public override V AcceptVisitor<V>(IBindingScopingVisitor<V> visitor)
            {
                return visitor.VisitScope(Scopes.SINGLETON);
            }

            public override void ApplyTo(IScopedBindingBuilder scopedBindingBuilder)
            {
                scopedBindingBuilder.In(Scopes.SINGLETON);
            }

            public override string ToString()
            {
                return Scopes.SINGLETON.ToString();
            }

            public override IScope ScopeInstance
            {
                get
                {
                    return Scopes.SINGLETON;
                }
            }
        }
        #endregion //end of Scoping implements
    }

}
